/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.properties;
import java.awt.BorderLayout;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.datatransfer.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.BeanInfo;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.Set;
import java.util.HashSet;
import java.util.AbstractCollection;
import java.util.ResourceBundle;
import java.util.Collections;
import java.util.Iterator;
import javax.swing.*;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import org.openide.loaders.*;
import org.openide.*;
import org.openide.util.datatransfer.*;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystem;
import org.openide.util.*;
import org.openide.util.enum.*;
import org.openide.util.actions.SystemAction;
import org.openide.util.actions.Presenter;
import org.openide.nodes.*;
/** Object that represents one FileEntry and has support for presentation of this entry as a node
*
* @author Jaroslav Tulach, Petr Jiricka
*/
public abstract class PresentableFileEntry extends FileEntry implements Node.Cookie {
/** generated Serialized Version UID */
//static final long serialVersionUID = 3328227388376142699L;
/** the node delegate for this data object */
private transient Node nodeDelegate;
/** Name of the template property. */
public static final String PROP_TEMPLATE = DataObject.PROP_TEMPLATE;
/** Name of the name property. */
public static final String PROP_NAME = DataObject.PROP_NAME;
/** Name of the modified property. */
public static final String PROP_MODIFIED = DataObject.PROP_MODIFIED;
/** Name of the property used during notification of changes in the set of cookies attached to this object. */
public static final String PROP_COOKIE = Node.PROP_COOKIE;
/** Name of valid property. Allows listening to deletion or disposal of the data object. */
public static final String PROP_VALID = DataObject.PROP_VALID;
/** Name of entry property. Allows listening to adding, deletion or renaming of the entry. */
public static final String PROP_ENTRY = "entry";
/** Modified flag */
private boolean modif = false;
/** property change listener support */
private transient PropertyChangeSupport changeSupport;
/** listener for changes in the cookie set */
private ChangeListener cookieL = new ChangeListener () {
public void stateChanged (ChangeEvent ev) {
firePropertyChange (PROP_COOKIE, null, null);
}
};
/** array of cookies for this entry */
private transient CookieSet cookieSet;
/** Creates new presentable file entry initially attached to a given file object.
* @param obj the data object this entry belongs to
* @param fo the file object for the entry
*/
public PresentableFileEntry(MultiDataObject obj, FileObject fo) {
super (obj, fo);
}
/** Creates a node delegate for this entry. */
protected abstract Node createNodeDelegate();
/** Get the node delegate. Either {@link #createNodeDelegate creates it} (if it does not
* already exist) or
* returns a previously created instance of it.
*
* @return the node delegate (without parent) for this data object
*/
public final Node getNodeDelegate () {
if (nodeDelegate == null) {
synchronized (this) {
if (nodeDelegate == null) {
nodeDelegate = createNodeDelegate ();
}
}
}
return nodeDelegate;
}
/** Package private method to assign template attribute to a file.
* Used also from FileEntry.
*
* @param fo the file
* @param newTempl is template or not
* @return true if the value change/false otherwise
*/
static boolean setTemplate (FileObject fo, boolean newTempl) throws IOException {
boolean oldTempl = false;
Object o = fo.getAttribute(DataObject.PROP_TEMPLATE);
if ((o instanceof Boolean) && ((Boolean)o).booleanValue())
oldTempl = true;
if (oldTempl == newTempl)
return false;
fo.setAttribute(DataObject.PROP_TEMPLATE, (newTempl ? new Boolean(true) : null));
return true;
}
/** Set the template status of this file object.
* @param newTempl <code>true</code> if the object should be a template
* @exception IOException if setting the template state fails
*/
public final void setTemplate (boolean newTempl) throws IOException {
if (!setTemplate (getFile(), newTempl)) {
// no change in state
return;
}
firePropertyChange(PresentableFileEntry.PROP_TEMPLATE, new Boolean(!newTempl), new Boolean(newTempl));
}
/** Get the template status of this data object.
* @return <code>true</code> if it is a template
*/
public boolean isTemplate () {
Object o = getFile().getAttribute(PROP_TEMPLATE);
boolean ret = false;
if (o instanceof Boolean)
ret = ((Boolean) o).booleanValue();
return ret;
}
/* Renames underlying fileobject. This implementation return the
* same file. Fires property change. Called when the DO is renamed, not the entry
*
* @param name new name
* @return file object with renamed file
*/
public FileObject rename (String name) throws IOException {
String oldName = getName();
FileObject fo = super.rename(name);
firePropertyChange(PresentableFileEntry.PROP_NAME, oldName, name);
return fo;
}
/* Renames underlying fileobject. This implementation return the
* same file. Fires property change. Called when the file entry is renamed, not the DO
*
* @param name new name
* @return file object with renamed file
*/
public FileObject renameEntry (String name) throws IOException {
return PresentableFileEntry.this.rename(name);
}
/* Deletes file object
*/
public void delete () throws IOException {
super.delete();
firePropertyChange(PresentableFileEntry.PROP_VALID, Boolean.FALSE, Boolean.TRUE);
}
/** Test whether the object may be deleted.
* @return <code>true</code> if it may
*/
public abstract boolean isDeleteAllowed ();
/** Test whether the object may be copied.
* @return <code>true</code> if it may
*/
public abstract boolean isCopyAllowed ();
/** Test whether the object may be moved.
* @return <code>true</code> if it may
*/
public abstract boolean isMoveAllowed ();
/** Test whether the object may create shadows.
* <p>The default implementation returns <code>true</code>.
* @return <code>true</code> if it may
*/
public boolean isShadowAllowed () {
return true;
}
/** Test whether the object may be renamed.
* @return <code>true</code> if it may
*/
public abstract boolean isRenameAllowed ();
/** Test whether the object is modified.
* @return <code>true</code> if it is modified
*/
public boolean isModified() {
return modif;
}
/** Set whether the object is considered modified.
* Also fires a change event.
* If the new value is <code>true</code>, the data object is added into a {@link #getRegistry registry} of opened data objects.
* If the new value is <code>false</code>,
* the data object is removed from the registry.
*/
public void setModified(boolean modif) {
if (this.modif != modif) {
this.modif = modif;
firePropertyChange(DataObject.PROP_MODIFIED, new Boolean(!modif), new Boolean(modif));
}
}
/** Get help context for this object.
* @return the help context
*/
public abstract HelpCtx getHelpCtx ();
/** Get the name of the data object.
* <p>The default implementation uses the name of the primary file.
* @return the name
*/
public String getName () {
return getFile ().getName ();
}
/** Get the folder this data object is stored in.
* @return the folder; <CODE>null</CODE> if the primary file
* is the {@link FileObject#isRoot root} of its filesystem
*/
public final DataFolder getFolder () {
FileObject fo = getFile ().getParent ();
// could throw IllegalArgumentException but only if fo is not folder
// => then there is a bug in filesystem implementation
return fo == null ? null : DataFolder.findFolder (fo);
}
//
// Property change support
//
/** @param l the listener
*/
public synchronized void addPropertyChangeListener (PropertyChangeListener l) {
getChangeSupport ().addPropertyChangeListener (l);
}
/** @param l the listener
*/
public void removePropertyChangeListener (PropertyChangeListener l) {
getChangeSupport ().removePropertyChangeListener (l);
}
/** Fires property change notification to all listeners registered via
* {@link #addPropertyChangeListener}.
*
* @param name of property
* @param oldValue old value
* @param newValue new value
*/
protected final void firePropertyChange (String name, Object oldValue, Object newValue) {
getChangeSupport ().firePropertyChange (name, oldValue, newValue);
}
/** Getter for standard property change support. This is used in
* this class and by this method provided to subclasses.
*
* @return support
*/
private synchronized final PropertyChangeSupport getChangeSupport () {
if (changeSupport == null) {
changeSupport = new PropertyChangeSupport (this);
}
return changeSupport;
}
/** Set the set of cookies.
* To the provided cookie set a listener is attached,
* and any change to the set is propagated by
* firing a change on {@link #PROP_COOKIE}.
*
* @param s the cookie set to use
*/
public synchronized void setCookieSet (CookieSet s) {
if (cookieSet != null) {
cookieSet.removeChangeListener (cookieL);
}
s.addChangeListener (cookieL);
cookieSet = s;
firePropertyChange (PROP_COOKIE, null, null);
}
/** Get the set of cookies.
* If the set had been
* previously set by {@link #setCookieSet}, that set
* is returned. Otherwise an empty set is
* returned.
*
* @return the cookie set (never <code>null</code>)
*/
public CookieSet getCookieSet () {
CookieSet s = cookieSet;
if (s != null) return s;
synchronized (this) {
if (cookieSet != null) return cookieSet;
// sets empty sheet and adds a listener to it
setCookieSet (new CookieSet ());
return cookieSet;
}
}
/** Look for a cookie in the current cookie set matching the requested class.
*
* @param type the class to look for
* @return an instance of that class, or <code>null</code> if this class of cookie
* is not supported
*/
public Node.Cookie getCookie (Class type) {
CookieSet c = cookieSet;
if (c != null) {
Node.Cookie cookie = c.getCookie (type);
if (cookie != null) return cookie;
}
if (type.isInstance (this)) {
return this;
}
return null;
}
/** Getter for a text from resource bundle.
*/
static String getString (String name) {
return NbBundle.getBundle ("org.netbeans.modules.properties.Bundle").getString (name);
}
}
/*
* <<Log>>
* 6 Gandalf 1.5 11/11/99 Jesse Glick Display miscellany.
* 5 Gandalf 1.4 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 4 Gandalf 1.3 8/17/99 Petr Jiricka Serialization
* 3 Gandalf 1.2 6/9/99 Ian Formanek ---- Package Change To
* org.openide ----
* 2 Gandalf 1.1 6/8/99 Petr Jiricka
* 1 Gandalf 1.0 5/12/99 Petr Jiricka
* $
*/